package cxt

import (
	"container/list"
	"github.com/name5566/leaf/chanrpc"
	"github.com/name5566/leaf/module"
	"lol.com/feb214-game/nest.git/leaf"
	"lol.com/feb214-game/nest.git/log"
	"sync"
	"sync/atomic"
	"talent.com/cube/common/msg"
	"talent.com/cube/conf"
	"talent.com/cube/conf/g"
)

//主协程
var (
	LobbySkeleton = leaf.NewSkeleton()
	LobbyRPC      = LobbySkeleton.ChanRPCServer

	userSyncChannels  []chan *SyncData
	robotSyncChannels []chan *SyncData
)

type SyncCounter struct {
	count int32
	uc    *UserContext
}

func NewSyncCounter(uc *UserContext) *SyncCounter {
	return &SyncCounter{
		count: 0,
		uc:    uc,
	}
}

//只要用户的未同步数据减到0，且用户离线，可以立刻释放用户的金币锁
func (sc *SyncCounter) Decr() int32 {
	left := atomic.AddInt32(&sc.count, -1)
	if left == 0 && atomic.LoadInt32(&sc.uc.Disconnect) == 1 {
		LobbyRPC.Go(g.EventReleaseGoldLock, sc.uc.ID, sc.uc.GameType)
	} else if left < 0 {
		log.Error("game %v user %v sync counter decr to %v, this must be a bug!!!",
			sc.uc.GameType, sc.uc.ID, left)
	}
	return left
}
func (sc *SyncCounter) Incr() int32 {
	return atomic.AddInt32(&sc.count, 1)
}
func (sc *SyncCounter) Cleared() bool {
	log.Info("releaseLock:%v", atomic.LoadInt32(&sc.count))
	return atomic.LoadInt32(&sc.count) == 0
}

type UserContext struct {
	*msg.UserST
	GameType        int32
	ClientVersion   int32
	RoomKind        int32
	TableID         int64
	LeftBankerTimes int
	RobotLevel      int // -1:真实用户，0-4，表明档次
	Disconnect      int32
	Tax             int64 //本局税收
	SleepUntil      int64 //机器人专用：计算休眠截至时间
	UnSync          int64
	//真实用户专用：未同步的扣钱次数；用户每次下注+1；对于结算时可能扣钱的游戏，第一次下注额外+1；坐庄用户需要+1
	CostSyncCounter *SyncCounter `json:"-"`
	SyncBet         int64
	BigWinCount     int32
}

func (uc *UserContext) IsRobot() bool {
	return uc.RobotLevel >= 0
}

// 接口，各游戏自行实现
type IWorld interface {
	Settle() *msg.AwardNT     //结算
	NextTS() int64            //计算下一阶段的时间戳
	CheckBetIndex(int32) bool //验证下注参数是否合法
}

//玩家列表的字段
type TopUserRO struct {
	ID        uint64  `json:"id"`
	UserName  string  `json:"user_name"`
	Avatar    string  `json:"avatar"`
	Gender    int32   `json:"gender"`
	Credit    float64 `json:"credit"`
	BetAmount float64 `json:"bet_amount"` //下注金额
	WinCount  int     `json:"win_count"`  //获胜次数
}

// 切牌后剩余的牌堆
type LeftCards struct {
	Data []int32
}

// LifeStats 房间生命周期统计
type LifeStats struct {
	Counter      int32 //房间生命周期内的开局次数(用于统计)
	LastActiveAt int64 //上一次开局时间戳（）
}

//对客户端是一个房间，但是对服务端是一个桌子
type TableContext struct {
	*msg.RoomST
	GameType          int32
	Skeleton          *module.Skeleton `json:"-"`
	RPC               *chanrpc.Server  `json:"-"`
	CloseChan         chan bool        `json:"-"`
	UserCount         int32            //场内真实用户人数
	Players           *SyncPlayersMap  `json:"-"` //真实用户+机器人
	TermPlan          *list.List       `json:"-"` //机器人本局出手计划
	WorldImpl         IWorld           `json:"-"` //游戏接口的实现
	HistoryWinners    *list.List       `json:"-"` //最近X局真正赢的用户
	HistoryBetters    *list.List       `json:"-"` //最近X局下注用户及其金额
	CostCache         map[uint64]int64 //下注成本，缓存以减少计算量
	IndexBetCache     map[int32]int64  //各索引上下注的累计，缓存以减少计算量
	RealIndexBetCache map[int32]int64
	TopUserSnap       []*TopUserRO            `json:"-"` //每次更新坐席时数据的快照
	UserBetBuffer     map[uint64]*msg.BetInfo `json:"-"` //周期内累计的用户下注
	Lc                *LeftCards              `json:"-"` //目前只有万人金花用到
	chipSet           map[int64]bool

	*LifeStats
	RealUserId     uint64               //真实用户id
	CheatId        int32                //策略命中id
	StrategyConf   conf.StrategyConfNew //当前策略
	NoUserCount    int32                //没有玩家就关闭桌子的累计次数
	RechargeRate   float64              //玩家充提比，需要在下注之前获取，避免数据错误
	RechargeAmount int64                //玩家充值金额，需要在下注之前获取，避免数据错误
}

type SyncPlayersMap struct {
	Map *sync.Map
}

func (players *SyncPlayersMap) GetPlayer(userId uint64) *UserContext {
	iplayer, ok := players.Map.Load(userId)
	if !ok {
		return nil
	}
	player := iplayer.(*UserContext)
	return player
}

func (players *SyncPlayersMap) SetPlayers(userId uint64, user *UserContext) {
	players.Map.Store(userId, user)
}

func (players *SyncPlayersMap) DeletePlayers(userId uint64) {
	players.Map.Delete(userId)
}

func (players *SyncPlayersMap) Length() int {
	var i int
	players.Map.Range(func(_ interface{}, _1 interface{}) bool {
		i += 1
		return true
	})
	return i
}

type SyncData struct {
	UserId         uint64
	GameType       int32
	RobotLevel     int
	Bet            int64
	TermBet        int64
	LogOut         bool
	Award          int64
	TermNumber     int64
	TableID        int64
	Left           int //每一局剩余结算人数
	WithdrawalRate float64
}

//同一期的数据应该使用同一个channel，这样才能保证顺序性
func GetTermChannel(termNumber uint64) chan *SyncData {
	return userSyncChannels[termNumber%g.UserSyncGoroutine]
}

//每个channel一个协程同步
func AllSyncChannel() []chan *SyncData {
	return append(userSyncChannels, robotSyncChannels...)
}

func init() {
	userSyncChannels = make([]chan *SyncData, g.UserSyncGoroutine)
	robotSyncChannels = make([]chan *SyncData, g.RobotSyncGoroutine)
	for i := 0; i < g.UserSyncGoroutine; i++ {
		userSyncChannels[i] = make(chan *SyncData, g.MaxUserSyncQueue)
	}
	for i := 0; i < g.RobotSyncGoroutine; i++ {
		robotSyncChannels[i] = make(chan *SyncData, g.MaxRobotSyncQueue)
	}
}

func (tc *TableContext) IsValidChip(chip int64) bool {
	if len(tc.chipSet) == 0 {
		tc.chipSet = make(map[int64]bool, len(tc.Chips))
		for _, amount := range conf.RoomConfig(tc.GameType, tc.Kind).ChoiceChips {
			tc.chipSet[amount] = true
		}
	}
	return tc.chipSet[chip]
}
